package vip.zhenzicheng.concurrent_program.thread.sgg.lock;

import java.util.concurrent.TimeUnit;

/**
 * 8种锁demo
 *
 * @author zhenzicheng
 * @date 2022-06-21 21:02
 */
public class Lock8Demo {

  /**
   * 加锁核心：线程操作资源类
   * <pre>
   * 1. 标准t1,t2两个线程访问，先打印发邮件
   * 2. sendEmail增加sleep，还是先发邮件,sleep不会释放锁
   * 3. 对于普通hello方法（无锁），先打印hello
   * 4. 有两个资源类，分别调用两个资源类的两个不同加锁方法，先打印sendSMS，因为该方法不等待
   * 5. 将sendEmail、sendSMS都改为静态同步方法,调用同一个资源类的两个加锁方法，还是先打印sendEmail
   * 6. 在5基础上，分别调用两个资源类的不同的同步静态方法，还是先打印sendEmail
   * 7. 一个静态同步方法，一个普通同步方法，同一个资源类，还是先打印sendSMS
   * 8. 在7基础上，有两个资源类，还是先打印sendSMS
   * <br/>
   * 总结：
   * 1-2 -> 一个对象里面如果有多个synchronized方法，某一个时刻一个同步方法被一个线程访问后就是对象锁，将整个对象（资源类）中所有同步方法都独占，但是普通方法可以访问。
   *        也即某一个时刻只能有唯一线程去访问synchronized方法，锁当前this对象。
   * 3-4 -> 普通方法与同步锁无关，换成两个对象后锁的是不同对象互不影响
   * 5-6 -> 1) 对于普通同步方法，锁的是this当前实例对象，所有同步方法都是用同一把锁->this
   *        2) 对于静态同步方法，锁的是当前Class对象，如Phone.class
   *        3) 对于同步方法块，锁的是synchronized括号内的对象
   *           例如：Object o = new Object();
   *                synchronized (o) {}
   * 7-8 -> 当一个线程试图访问同步代码时必须先得到所，正常退出或者抛出异常时必须释放锁
   *        所有的对象锁都是锁的this对象，就是new出来的具体对象，所有类锁锁的是Class对象
   */
  public static void main(String[] args) {
    Phone phone = new Phone(); // 创建资源类
    Phone phone2 = new Phone();

    new Thread(() -> {
      phone.sendEmail();
    }, "t1").start();
    try {
      TimeUnit.MILLISECONDS.sleep(200);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    new Thread(() -> {
      // phone.sendSMS();
      // phone.hello();
      phone2.sendSMS();
      // phone2.sendEmail();
    }, "t2").start();

  }
}

// 资源类
class Phone {

  public static synchronized void sendEmail() {
    System.out.println("线程[" + Thread.currentThread().getName() + "] is coming");
    try {
      TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("----sendEmail");
  }

  public synchronized void sendSMS() {
    System.out.println("----sendSMS");
  }

  // 普通方法
  public void hello() {
    System.out.println("----hello");
  }

}
